/* Eshm - SHM abstraction
 * Copyright (C) 2008-2012 Jorge Luis Zapata
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 * If not, see <http://www.gnu.org/licenses/>.
 */
#include "Eshm.h"
#include "eshm_private.h"

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* TODO
 * for locks we need to use the posix semaphores
 */
/*============================================================================*
 *                                  Local                                     *
 *============================================================================*/
typedef struct _Eshm_Segment_Posix
{
	char *name;
	size_t size;
	void *mapped;
	int fd;
} Eshm_Segment_Posix;
/*----------------------------------------------------------------------------*
 *                          The segment interface                             *
 *----------------------------------------------------------------------------*/
static void * _posix_map_get(void *data)
{
	Eshm_Segment_Posix *thiz = data;
	return thiz->mapped;
}

static void _posix_free(void *data)
{
	Eshm_Segment_Posix *thiz = data;

	if (thiz->mapped)
		munmap(thiz->mapped, thiz->size);
	if (thiz->fd)
	{
		shm_unlink(thiz->name);
		close(thiz->fd);
	}
	free(thiz);
}

static Eshm_Segment_Descriptor _descriptor = {
	/* .map_get	= */ _posix_map_get,
	/* .free 	= */ _posix_free,
};
/*============================================================================*
 *                                 Global                                     *
 *============================================================================*/
/*============================================================================*
 *                                   API                                      *
 *============================================================================*/
EAPI Eshm_Segment * eshm_segment_posix_new(const char *name, Eina_Bool create,
		size_t size)
{
	Eshm_Segment_Posix *thiz;
	Eshm_Segment *s;
	int fd;
	void *mapped;
	int ps;
	int rest;
	int flags = O_RDWR;

	/* sanitize the size */
	ps = getpagesize();
	rest = size % ps;
	if (rest)
		size += rest;

	if (create)
		flags |= O_CREAT;

	/* first try to get it */
	/* TODO fix the mask ... */
	fd = shm_open(name, flags, 00777);
	if (fd < 0)
	{
		ERR("Impossible to shm_open: '%s'", strerror(errno));
		return NULL;
	}
	/* TODO how to know if it was really created */
	if (ftruncate(fd, size) < 0)
	{
		ERR("Impossible to ftruncate: '%s'", strerror(errno));
		goto err;
	}
	/* try to map it */
	DBG("Mapping with fd %d, size %zu", fd, size);
	mapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (!mapped)
	{
		ERR("Impossible to mmap: '%s'", strerror(errno));
		goto err;
	}

	thiz = calloc(1, sizeof(Eshm_Segment_Posix));
	thiz->fd = fd;
	thiz->size = size;
	thiz->name = strdup(name);
	thiz->mapped = mapped;

	s = eshm_segment_new(&_descriptor, size, thiz);

	return s;
err:
	shm_unlink(name);
	close(fd);
	return NULL;
}

EAPI const char * eshm_segment_posix_name_get(Eshm_Segment *s)
{
	Eshm_Segment_Posix *thiz;

	thiz = eshm_segment_data_get(s);
	return thiz->name;
}
